#include <iostream>
#include <vector>
#include <iomanip>
#include <cmath>
#include <string>
#include <cstring>
#include <cstdlib>
#include <fstream>
//#include <boost/program_options.hpp>

#include "muParser.h"

#include "CrossPlatform.hpp"
#include "Specimen.hpp"
#include "ProportionalEnv.hpp"
#include "TournamentEnv.hpp"
#include "AckleyFunction.hpp"
#include "EvolutionaryException.hpp"
#include "GeemFunction.hpp"
#include "RosenbrockFunction.hpp"
#include "HimmelblauFunction.hpp"
#include "GoldsteinPriceFunction.hpp"
#include "RastiginFunction.hpp"

using std::cout;
using std::cerr;
using std::cin;
using std::endl;
using std::string;



typedef enum{
  ACKLEY=0,
  GEEM,
  GOLDSTEIN_PRICE,
  HIMMELBLAU,
  RASTRIGIN,
  ROSENBROCK,
  FUNCTION_OWN,
  FUNCTION_NOT_SET
}EvalNames;

typedef enum{
  PROPORTIONAL_LIN,
  PROPORTIONAL_EXP,
  PROPORTIONAL_OWN,
  TOURNAMENT_RET,
  TOURNAMENT_NRET,
  ENVIRONMENT_NOT_SET
}EnvNames;

typedef enum{
  ITER_SET=1,
  MUT_PROB_SET=(1<<1),
  CRO_PROP_SET=(1<<2),
  CRO_PROB_SET=(1<<3),
  DIMS_SET=(1<<4),
  EVAL_SET=(1<<5),
  LIMITS_SET=(1<<6),
  SELECT_SET=(1<<7),
  POP_SET=(1<<8)
}FlagValues;

void analyseFlags(const unsigned int &flags)
{
  if(!(flags & ITER_SET))
    {
      cout<<"iterations ";
    }
  else if(!(flags & MUT_PROB_SET))
    {
      cout<<"mutation probability ";
    }
  else if(!(flags & CRO_PROB_SET))
    {
      cout<<"crossover probabbility ";
    }
  else if(!(flags && CRO_PROP_SET))
    {
      cout<<"crossover porportion ";
    }
  else if(!(flags & DIMS_SET))
    {
      cout<<"dimensionality ";
    }
  else if(!(flags & EVAL_SET))
    {
      cout<<"evaluation function ";
    }
  else if(!(flags & LIMITS_SET))
    {
      cout<<"limits ";
    }
  else if(!(flags & SELECT_SET))
    {
      cout<<"selection ";
    }
  else if(!(flags & POP_SET))
    {
      cout<<"population ";
    }
  else
    {
      cout<<"somethings wrong"<<endl<<"unknown flag "<<(0xFFF & flags);
    }
  cout<<"not set"<<endl;
}

  
bool isNumber(const std::string str)
{
  bool result=false;
  if(str.size()==strspn(str.c_str(),"+-0123456789"))
    {
      result=true;
    }
  return result;
}

EvalFunction *evalFunctionFactory(const EvalNames &name,
				  const unsigned &dimensions,
				  std::string equation=" ")
{
  
  EvalFunction *result=NULL;

  switch(name)
    {
    case ACKLEY:
      {
	result=new AckleyFunction(dimensions);
	break;
      }
    case GEEM:
      {
	result=new GeemFunction;
	break;
      }
    case GOLDSTEIN_PRICE:
      {
	result=new GoldsteinPriceFunction;
	break;
      }
    case HIMMELBLAU:
      {
	result=new HimmelblauFunction;
	break;
      }
    case RASTRIGIN:
      {
	result=new RastiginFunction(dimensions);
	break;
      }
    case ROSENBROCK:
      {
	result=new RosenbrockFunction;
	break;
      }
    case FUNCTION_OWN:
      {
	//tu bedzie funktor Natalii
      }
    case FUNCTION_NOT_SET:
      {
	EvolutionaryException ex(0,"evaluation function has not bee set",
				 __func__,__FILE__,__LINE__);
	throw ex;
      }
    }
  return result;
}

Environment *environmentFactory(const EnvNames &envName,
				std::vector<Gene> &mins,
				std::vector<Gene> &maxs,
				unsigned int maxPopulation,
				double mutationProbability,
				double crossoverProbability,
				double crossoverProportion,
				unsigned dimensions,
				unsigned maxIterations,
				double epsilonX=0,
				double epsilonY=0,
				std::string equation=" ")
{
  Environment *environment=NULL;
  switch(envName)
    {
    case PROPORTIONAL_LIN:
      {
	ProportionalEnv *env=new ProportionalEnv;
	env->setScalingFunction(new LinearScaling);
	environment = env;
	break;
      }
    case PROPORTIONAL_EXP:
      {
	ProportionalEnv *env=new ProportionalEnv;
	env->setScalingFunction(new ExponentialScaling);
	environment = env;
	break;
      }
    case PROPORTIONAL_OWN:
      {
	// miejsce do popisu dla Natalii
	//environment = env;
	break;
      }
    case TOURNAMENT_RET:
      {
	TournamentEnv *env=new TournamentEnv;
	env->setIfReturning(true);
	environment = env;
	break;
      }
    case TOURNAMENT_NRET:
      {
	TournamentEnv *env=new TournamentEnv;
	env->setIfReturning(true);
	environment = env;
	break;
      }
    case ENVIRONMENT_NOT_SET:
      {
	EvolutionaryException ex(0,"selection type not set",
				 __func__,__FILE__,__LINE__);
	throw ex;
      }

    }
  environment->setMaxPopulation(maxPopulation);
  environment->setMaxIterations(maxIterations);
  environment->setMutationProbability(mutationProbability);
  environment->setCrossoverProbability(crossoverProbability);
  environment->setAlgebraicCrossoverProportion(crossoverProportion);
  environment->setDimensionality(dimensions);
  environment->setMaxX(maxs);
  environment->setMinX(mins);
  
  if(epsilonY)
    {
      environment->setFitnessMinChange(epsilonY);
    }	  
  if(epsilonX)
    {
      environment->setCoordinatesMinChange(epsilonX);
    }

  return environment;
}

void analyseEvolution(Environment *environment,
		      EvalFunction *evalFunction,
		      std::ofstream &stream)
{
  Specimen theBestOne;
	
  if(stream.is_open())
    {
      theBestOne=environment->search(*evalFunction,stream);
    }
  else
    {
      theBestOne=environment->search(*evalFunction);
    }
	
  for(unsigned j=0;j<theBestOne.getDimensions();++j)
    {
      cout<<theBestOne[j]<<" ";
    }
	
  cout<<" fitness: "<<theBestOne.getFitness()<<endl;

}
				
void printHelp(const std::string &exeName)
{
  cout<<"usage: "<<exeName<<"([-x epsX] [-y epsY] -i iterations -P maxPop\n"
    "-m mutProb -p crossProp -c crossProb -d dims (-F function(x,y,z,t,w|-f"
    "(Ackley|Geem|GoldsteinPrice|Rastrigin|Rosenbrock))) -l limits "
    "-e ((proportional (linear|exponential|(own scaling(x))))|(tournament ("
    "returning|nreturning))) [-o outputFile])| -h"<<endl;  

}


int main(int argc,char **argv)
{
  //  string buff;
  bool xSet=false;
  bool ySet=false;
  bool functionSet=false;
  bool dimsSet=false;
  bool boundsSet=false;
  bool returning=true;
  bool envSet=false;
  
  unsigned int flags=0;

  double epsilonX=0;
  double epsilonY=0;
  double mutationProbability=0;
  double crossoverProportion=0;
  double crossoverProbability=0;
  unsigned dimensionality=0;
  unsigned maxIterations=0;
  unsigned maxPopulation=0;
  std::vector<Gene> maxs;
  std::vector<Gene> mins;

  std::string equation="";
  std::string scalingEquation="";
  std::string fileName="";
  std::vector<std::string> functions;
  functions.push_back("Ackley");
  functions.push_back("Geem");
  functions.push_back("Goldstein-Price");
  functions.push_back("Himmelblau");
  functions.push_back("Rastigirin");
  functions.push_back("Rosenbrock");
  
  EvalFunction *evalFunction=NULL;
  EvalNames evalName=FUNCTION_NOT_SET;
  EnvNames envName=ENVIRONMENT_NOT_SET;

  Environment *environment=NULL;

  std::ofstream stream;

  if(argc>1)
    {
      try
	{
	  for(int i=0;i<argc;++i)
	    {
	      if(0==strcmp("-x",argv[i]))
		{//min coordinates change
		  if(i+1<argc)
		    {
		      epsilonX=atof(argv[++i]);
		      xSet=true;
		      if(ySet)
			{
			  boundsSet=true;
			  //			  flags|=LIMITS_SET;
			}
		    }
		  else
		    {
		      EvolutionaryException ex(0,"incomplete argument \"-x\"",
					       __func__,__FILE__,__LINE__);
		      //  flags^=~LIMITS_SET;
		      xSet=false;
		      throw ex;
		    }
		}
	      else if(0==strcmp("-y",argv[i]))
		{//min fitness change
		  if(i+1<argc)
		    {
		      epsilonY=atof(argv[++i]);
		      ySet=true;
		      if(xSet)
			{
			  boundsSet=true;
			  //  flags|=LIMITS_SET;
			}
		    }
		  else
		    {
		      EvolutionaryException ex(0,"incomplete argument \"-y\"",
					       __func__,__FILE__,__LINE__);
		      //flags^=~LIMITS_SET;
		      ySet=false;
		      throw ex;
		    }
		}
	      else if(0==strcmp("-o",argv[i]))
		{//setting output
		  if(i+1<argc)
		    {
		      if(stream.is_open())
			{
			  stream.close();
			}
		      stream.open(argv[i+1]);
		      if(!stream.is_open())
		      {
			EvolutionaryException ex(0,("couldn't open file"
						    +string(argv[i+1])).c_str(),
						 __func__,__FILE__,__LINE__);
			throw ex;
			
		      }
		      //		      i=i+2;
		    }
		  else
		    {
		      EvolutionaryException ex(0,"incomplete argument \"-o\"",
					       __func__,__FILE__,__LINE__);
		      throw ex;
		    }
		}
	      else if(0==strcmp("-i",argv[i]))
		{//max iterations
		  if(i+1<argc)
		    {
		      maxIterations=atol(argv[i+1]);
		      flags|=ITER_SET;
		    }
		  else
		    {
		      EvolutionaryException ex(0,"incomplete argument \"-i\"",
					       __func__,__FILE__,__LINE__);
		      flags^=~ITER_SET;
		      throw ex;
		    }
		}
	      else if(0==strcmp("-P",argv[i]))
		{//max population
		  if(i+1<argc)
		    {
		      maxPopulation=atol(argv[i+1]);
		      flags|=POP_SET;
		    }
		  else
		    {
		      EvolutionaryException ex(0,"incomplete argument \"-P\"",
					       __func__,__FILE__,__LINE__);
		      flags^=~POP_SET;
		      throw ex;
		    }
		}
	      else if(0==strcmp("-m",argv[i]))
		{//mutation probability
		  if(i+1<argc)
		    {
		      mutationProbability=atof(argv[i+1]);
		      flags|=MUT_PROB_SET;
		    }
		  else
		    {
		      EvolutionaryException ex(0,"incomplete argument \"-m\"",
					       __func__,__FILE__,__LINE__);
		      flags^=~MUT_PROB_SET;
		      throw ex;
		    }
		}
	      else if(0==strcmp("-p",argv[i]))
		{//crossover proportion
		  if(i+1<argc)
		    {
		      crossoverProportion=atof(argv[i+1]);
		      flags|=CRO_PROP_SET;
		    }
		  else
		    {
		      EvolutionaryException ex(0,"incomplete argument \"-c\"",
					       __func__,__FILE__,__LINE__);
		      flags^=~CRO_PROP_SET;
		      throw ex;
		    }
		}
	      else if(0==strcmp("-h",argv[i]))
		{//help
		  printHelp(argv[0]);
		}
	      else if(0==strcmp("-c",argv[i]))
		{//crossover probability
		  if(i+1<argc)
		    {
		      crossoverProbability=atof(argv[i+1]);
		      flags|=CRO_PROB_SET;
		    }
		  else
		    {
		      EvolutionaryException ex(0,"incomplete argument \"-c\"",
					       __func__,__FILE__,__LINE__);
		      flags^=~CRO_PROB_SET;
		      throw ex;
		    }
		}
	      else if(0==strcmp("-d",argv[i]))
		{//number of dimensions
		  if(i+1<argc)
		    {
		      if(!(0u==dimensionality && !dimsSet)
			 ||((0u!=dimensionality)
			    &&(atoi(argv[i+1])!=dimensionality)))
			{
			  EvolutionaryException ex(0,"number of dimensions set "
						   "and previously passed "
						   "inconsistent",
						   __func__,__FILE__,__LINE__);
			  flags^=~DIMS_SET;
			  throw ex;
			}
		      else
			{
			  dimensionality=atoi(argv[++i]);
			  flags|=DIMS_SET;
			}
		  }
		else
		  {
		    EvolutionaryException ex(0,"incomplete argument \"-d\"",
					     __func__,__FILE__,__LINE__);
		    flags^=~DIMS_SET;
		    throw ex;
		  }
	      }
	    else if(0==strcmp("-F",argv[i]))
	      {//function equation
		if(i+2<argc)
		  {
		    equation=std::string(argv[1+i]);
		    evalName=FUNCTION_OWN;
		    flags|=EVAL_SET;
		    i+=2;
		  }
		else
		  {
		    EvolutionaryException ex(0,"incomplete argument \"-F\"",
					     __func__,__FILE__,__LINE__);
		    flags^=~EVAL_SET;
		    throw ex;
		  }
	      }
	    else if(0==strcmp("-l",argv[i]))
	      {//limits
		Gene temp;
		int counter=0;
		int iterCount=0;
		//		while(i+2<argc && NULL==strpbrk(argv[i+1],'') 
		//    && NULL==strchr(argv[i+2],'-'))
		while((i+iterCount+2<argc) &&
		      isNumber(argv[i+iterCount+1]) && 
		      isNumber(argv[i+iterCount+2]))
		  {
		    temp=atof(argv[++iterCount+i]);
		    mins.push_back(temp);
		    temp=atof(argv[++iterCount+i]);
		    maxs.push_back(temp);
		    ++counter;
		  }
		if(0==counter)
		  {
		    EvolutionaryException ex(0,"-l inapropriate limits passed ",
					     __func__,__FILE__,__LINE__);
		    flags^=~LIMITS_SET;
		    throw ex;
		  }

		flags|=LIMITS_SET;

		if(dimsSet)
		  {
		    if(counter != dimensionality)
		      {
			EvolutionaryException ex(0,"number of dimensions set "
						 "and passed in limits "
						 "inconsistent",
						 __func__,__FILE__,__LINE__);
			flags^=~LIMITS_SET;
			throw ex;
		      }
		  }
		else
		  {
		    dimensionality=counter;
		    flags|=DIMS_SET;
		  }
	      }
	    else if(0==strcmp("-f",argv[i]))
	      {//function name 
		if(argc>i+1)
		  {
		    if(0==strcmp(argv[i+1],"Ackley"))
		      {
			evalName=ACKLEY;
			//i+=2;
			flags|=EVAL_SET;
		      }
		    else if(0==strcmp(argv[i+1],"Geem"))
		      {
			evalName=GEEM;
			//i+=2;
			flags|=EVAL_SET;
		      }
		    else if(0==strcmp(argv[i+1],"GoldPri"))
		      {
			evalName=GOLDSTEIN_PRICE;
			//i+=2;
			flags|=EVAL_SET;
		      }
		    else if(0==strcmp(argv[i+1],"Rastrigin"))
		      {
			evalName=RASTRIGIN;
			//i+=2;
			flags|=EVAL_SET;
		      }
		    else if(0==strcmp(argv[i+1],"Rosenbrock"))
		      {
			evalName=ROSENBROCK;
			//i+=2;
			flags|=EVAL_SET;
		      }
		    else
		      {
			EvolutionaryException ex(0,"-f incorrect function name",
						 __func__,__FILE__,__LINE__);
			flags^=~EVAL_SET;
			throw ex;
		      }

		  }
	      }
	    else if(0==strcmp("-e",argv[i]))
	      {//environment type
		if(argc>i+1)
		  {
		    if(0==strcmp(argv[i+1],"proportional"))
		      {		
			if(argc>i+2)
			  {
			    if(0==strcmp(argv[i+2],"linear"))
			      {
				envName=PROPORTIONAL_LIN;
				flags|=SELECT_SET;
			      }
			    else if(0==strcmp(argv[i+2],"exponential"))
			      {
				envName=PROPORTIONAL_EXP;
				flags|=SELECT_SET;
			
			      }//tu może, jesli Natalia zrobi, będzie mozliwość 
			    //sparsowania sobie funkcji proporcji
			    else if(0==strcmp(argv[i+2],"own"))
			      {
				if(i+3<argc)
				  {
				    envName=PROPORTIONAL_OWN;
				    scalingEquation=std::string(argv[i+3]); 
				    flags|=SELECT_SET;
				    
				  }
			      }
			  }
			//		    environment=env;
		      }
		    else if(0==strcmp(argv[i+1],"tournament"))
		      {
			TournamentEnv *env=new TournamentEnv;
			envName=TOURNAMENT_RET;
			if(argc>i+2)
			  {
			    if(0==strcmp(argv[i+2],"returning"))
			      {
				//env->setIfReturning(true);
				flags|=SELECT_SET;
			      }
			    else if(0==strcmp(argv[i+2],"nreturning"))
			      {
				//env->setIfReturning(false);
				envName=TOURNAMENT_NRET;
				flags|=SELECT_SET;
				returning=false;
			      }
			  }
			//		    environment=env;
		      } 
		    else 
		      {
			EvolutionaryException ex(0,"-e incorrect environment name",
						 __func__,__FILE__,__LINE__);
			flags^=~SELECT_SET;
			throw ex;
	
		      }
		  }
		else
		  {
		    EvolutionaryException ex(0,"-e incomplete option",
					     __func__,__FILE__,__LINE__);
		    flags^=~SELECT_SET;
		    throw ex;
		  }
	      }
	    }
	  if(0x1FF==flags)
	    {
	      evalFunction=evalFunctionFactory(evalName,dimensionality,
					       equation);
	      
	      environment=environmentFactory(envName,mins,maxs,
					     maxPopulation,
					     mutationProbability,
					     crossoverProbability,
					     crossoverProportion,
					     dimensionality,maxIterations,
					     epsilonX,epsilonY,equation);
	      
	      cout
		<<"Max population: "<<maxPopulation<<endl
		<<"Max iterations: "<<maxIterations<<endl
		<<"Mutation probability: "<<mutationProbability<<endl
		<<"Crossover proportion: "<<crossoverProportion<<endl
		<<"Crossover probability: "<<crossoverProbability<<endl
		<<"Dimensions: "<<dimensionality<<endl
		<<"Limits: ";
	      for(unsigned int i=0;i<mins.size();++i)
		{
		  cout<<"[ "<<mins[i]<<"; "<<maxs[i]<<"]";
		}
	      cout
		<<"Evaluation function: ";
	      switch(evalName)
		{
		case ACKLEY: cout<<"Ackley"; break;
		case GEEM: cout<<"Geem"; break;
		case GOLDSTEIN_PRICE: cout<<"Goldstein - Price"; break;
		case HIMMELBLAU: cout<<"Himmelblau"; break;
		case RASTRIGIN: cout<<"Rastrigin"; break;
		case ROSENBROCK: cout<<"Rosenbrock"; break;
		case FUNCTION_OWN: cout<<equation; break;
		}
	      
	      cout
		<<"Selection type: ";
	      switch(envName)
		{
		case PROPORTIONAL_LIN: 
		  cout<<"proportional with linear scaling";
		  break;
		case PROPORTIONAL_EXP: 
		  cout<<"proportional with exponential scaling";
		  break;
		case PROPORTIONAL_OWN: 
		  cout<<"proportional with "<<scalingEquation<<" scaling";
		  break;
		case TOURNAMENT_RET:
		  cout<<"tournament with returning";
		  break;
		case TOURNAMENT_NRET: ;
		  cout<<"tournament without returning";
		  break;

		}
	      cout<<endl;
	      if(0!=epsilonX)
		{
		  cout<<"Min x change: "<<epsilonX<<endl;
		}
	      if(0!=epsilonY)
		{
		  cout<<"Min y change: "<<epsilonY<<endl;
		}
	      analyseEvolution(environment,evalFunction,stream);
	      stream.close();
	      
	    }
	  else
	    {
	      analyseFlags(flags);
	      cout
		<<"Not enough arguments"<<endl
		<<"Flags: "<<flags<<endl
		<<"Max population: "<<maxPopulation<<endl
		<<"Max iterations: "<<maxIterations<<endl
		<<"Mutation probability: "<<mutationProbability<<endl
		<<"Crossover proportion: "<<crossoverProportion<<endl
		<<"Crossover probability: "<<crossoverProbability<<endl
		<<"Dimensions: "<<dimensionality<<endl
		<<"Limits: ";
	      for(unsigned int i=0;i<mins.size();++i)
		{
		  cout<<"[ "<<mins[i]<<"; "<<maxs[i]<<"]";
		}
	      cout<<endl
		<<"Evaluation function: ";
	      switch(evalName)
		{
		case ACKLEY: cout<<"Ackley"; break;
		case GEEM: cout<<"Geem"; break;
		case GOLDSTEIN_PRICE: cout<<"Goldstein - Price"; break;
		case HIMMELBLAU: cout<<"Himmelblau"; break;
		case RASTRIGIN: cout<<"Rastrigin"; break;
		case ROSENBROCK: cout<<"Rosenbrock"; break;
		case FUNCTION_OWN: cout<<equation; break;
		case FUNCTION_NOT_SET: cout<<"not set"; break;
		}
	      
	      cout<<endl
		<<"Selection type: ";
	      switch(envName)
		{
		case PROPORTIONAL_LIN: 
		  cout<<"proportional with linear scaling";
		  break;
		case PROPORTIONAL_EXP: 
		  cout<<"proportional with exponential scaling";
		  break;
		case PROPORTIONAL_OWN: 
		  cout<<"proportional with "<<scalingEquation<<" scaling";
		  break;
		case TOURNAMENT_RET:
		  cout<<"tournament with returning";
		  break;
		case TOURNAMENT_NRET: 
		  cout<<"tournament without returning";
		  break;
		case ENVIRONMENT_NOT_SET:
		  cout<<"environment not set";
		  break;
		}
	      cout<<endl;
	      if(0!=epsilonX)
		{
		  cout<<"Min x change: "<<epsilonX<<endl;
		}
	      if(0!=epsilonY)
		{
		  cout<<"Min y change: "<<epsilonY<<endl;
		}
	    }
	}
    
      catch(std::exception &ex)
	{
	  std::cerr<<ex.what()<<std::endl;
	  return EXIT_FAILURE;
	}
    }
  else
    {
      unsigned int option=99;
      while(0!=option)
	{
	  cout
	    <<"1.  Set max number of iterations ("<<maxIterations<<")"<<endl
	    <<"2.  Set mutation probability ("<<mutationProbability<<")"<<endl
	    <<"3.  Set crossover proportion ("<<crossoverProportion<<")"<<endl
	    <<"4.  Set crossover probability ("<<crossoverProbability<<")"<<endl
	    <<"5.  Set number of dimensions of problem ("<<dimensionality<<")"
	    <<endl
	    <<"6.  Set evaluated function (";
	  switch(evalName)
	    {
	    case ACKLEY:
	      cout<<"Ackley";
	      break;
	    case GEEM:
	      cout<<"Geem";
	      break;
	    case GOLDSTEIN_PRICE:
	      cout<<"Goldstein - Price";
	      break;
	    case HIMMELBLAU:
	      cout<<"Himmelblau";
	      break;
	    case RASTRIGIN:
	      cout<<"Rastrigin";
	      break;
	    case ROSENBROCK:
	      cout<<"Rosenbrock";
	      break;
	    case FUNCTION_OWN:
	      cout<<equation;
	      break;
	    case FUNCTION_NOT_SET:
	      cout<<"not set";
	    }
	  
	  cout
	    <<")"<<endl
	    <<"7.  Set limits for problem discipline (";
	  if(0!=mins.size())
	    {
	      for(unsigned int i=0;i<mins.size();++i)
		{
		  cout<<"["<<mins[i]<<"; "<<maxs[i]<<"]";
		}
	    }
	  else
	    {
	      cout<<"not set";
	    }
	  cout<<")"<<endl
	      <<"8.  Set selection type (";
	  switch(envName)
	    {
	    case PROPORTIONAL_LIN:
	      cout<<"proportional with linear scaling";
	      break;
	    case PROPORTIONAL_EXP:
	      cout<<"proportional with exponential scaling";
	      break;
	    case PROPORTIONAL_OWN:
	      cout<<"proportional with scaling using function "
		  <<scalingEquation;
	      break;
	    case TOURNAMENT_RET:
	      cout<<"tournament with returning";
	      break;
	    case TOURNAMENT_NRET:
	      cout<<"tournament without returning";
	      break;
	    case ENVIRONMENT_NOT_SET:
	      cout<<"not set";
	    }
	  
	  cout<<")"<<endl
	      <<"9. Set max population ("<<maxPopulation<<")"<<endl
	      <<"10.  Start simulation"<<endl
	      <<endl
	      <<"11. Set minimum best specimens coordinates change"
	    " (not mandatory) ("<<epsilonX<<")"<<endl
	      <<"12. Set minimum best specimens function value  change (not"
	    " mandatory) ("<<epsilonY<<")"<<endl
	      <<"13. Set output file (not mandatory) (";
	  if(0!=fileName.size())
	    {
	      cout<<fileName;
	    }
	  else
	    {
	      cout<<"not set";
	    }

	  cout<<")"
	      <<endl
	      <<"0.  Quit"
	      <<endl;
	  std::cin>>option;
	  if(!cin.good())
	    {		
	      cerr<<"Error, couldn't read the value"<<endl;
	      cin.clear();
	    }
	  else
	    {
	      switch(option)
		{
		case 1:
		  {
		    cout<<"Max number of iterations: ";
		    cin>>maxIterations;
		    if(!cin.good())
		      {		
			cerr<<"Error, couldn't read the value"<<endl;
			maxIterations=0;
			cin.clear();
		      }
		
		    break;
		  }
		case 2:
		  {
		    cout<<"mutation probability: ";
		    cin>>mutationProbability;
		    if(!cin.good())
		      {		
			cerr<<"Error, couldn't read the value"<<endl;
			mutationProbability=0;
			cin.clear();
		      }
		    break;
		  }
		case 3:
		  {
		    cout<<"crossover porportion (a*A+(1-a)*B): ";
		    cin>>crossoverProportion;
		    if(!cin.good())
		      {		
			cerr<<"Error, couldn't read the value"<<endl;
			crossoverProportion=0;
			cin.clear();
		      }
		    break;
		  }
		case 4:
		  {
		    cout<<"crossover probability: ";
		    cin>>crossoverProbability;
		    if(!cin.good())
		      {		
			cerr<<"Error, couldn't read the value"<<endl;
			crossoverProbability=0;
			cin.clear();
		      }
		    break;
		  }
		case 5:
		  {
		    cout<<"number of problem dimensions: ";
		    cin>>dimensionality;
		    if(!cin.good())
		      {		
			cerr<<"Error, couldn't read the value"<<endl;
			dimensionality=0;
			cin.clear();
		      }
		
		    break;
		  }
		case 6:
		  {
		    unsigned int eval=99;
		    cout<<"Evaluated function:"<<endl
			<<"1. Ackley"<<endl
			<<"2. Geem"<<endl
			<<"3. Goldstein-Price"<<endl
			<<"4. Himmelblau"<<endl
			<<"5. Rastrigin"<<endl
			<<"6. Rosenbrock"<<endl
			<<"7. Write equation"<<endl;

		    cin>>eval;
		    if(!cin.good())
		      {		
			cerr<<"Error, couldn't read the value"<<endl;
			evalName=FUNCTION_NOT_SET;
			cin.clear();
		      }
		    switch(eval)
		      {
		      case 1:
			evalName=ACKLEY;
			break;
		      case 2:
			evalName=GEEM;
			break;
		      case 3: 
			evalName=GOLDSTEIN_PRICE;
			break;
		      case 4:
			evalName=HIMMELBLAU;
			break;
		      case 5:
			evalName=RASTRIGIN;
			break;
		      case 6: 
			evalName=ROSENBROCK;
			break;
		      case 7:
			evalName=FUNCTION_OWN;
			break;
		      }
		
		    break;
		  }
		case 7:
		  {
		    if(0==dimensionality)
		      {
			cout<<"Set number of dimensions first"<<endl;
		      }
		    else
		      {
			mins.resize(dimensionality);
			maxs.resize(dimensionality);
			for(unsigned int i=0;i<dimensionality;++i)
			  {
			    cout<<"x"<<i<<"_min: ";
			    cin>>mins[i];
			    if(cin.good())
			      {
				cout<<"x"<<i<<"_max: ";
				cin>>maxs[i];
				if(!cin.good())
				  {
				    cerr<<"Error, couldn't read the value"<<endl;
				    mins.clear();
				    maxs.clear();
				    i=dimensionality;
				    cin.clear();
				  }
			      }
			    else
			      {
				cerr<<"Error, couldn't read the value"<<endl;
				mins.clear();
				maxs.clear();
				i=dimensionality;
				cin.clear();
			      }
			
			  }
		      }
		    break;
		  }
		case 8:
		  {
		    unsigned int selection=99;
		    cout
		      <<"1. proportional without scaling"<<endl
		      <<"2. proportional with linear scaling"<<endl
		      <<"3. proportional with exponential scaling"<<endl
		      <<"4. proportional with my own scaling function"<<endl
		      <<"5. tournament with returning"<<endl
		      <<"6. tournament without returning"<<endl;
		
		    cin>>selection;
		    if(!cin.good())
		      {		
			cerr<<"Error, couldn't read the value"<<endl;
			envName=ENVIRONMENT_NOT_SET;
			cin.clear();
		      }

		    switch(selection)
		      {
		      case 1: 
			envName=PROPORTIONAL_LIN; 
			break;
		      case 2:
			envName=PROPORTIONAL_EXP;
			break;
		      case 3:
			envName=PROPORTIONAL_OWN;
			break;
		      case 4:
			envName=TOURNAMENT_RET;
			break;
		      case 5:
			envName=TOURNAMENT_NRET;
		      }
		  
		    break;
		  }
		case 9:
		  {
		    cout<<"max population: ";
		    cin>>maxPopulation;
		    if(!cin.good())
		      {		
			cerr<<"Error, couldn't read the value"<<endl;
			maxPopulation=0;
			cin.clear();
		      }

		    break;
		  }
		case 10:
		  {
		    try{

		      if(1/*0xFF==flags*/)
			  {
			    evalFunction=evalFunctionFactory(evalName,
							     dimensionality,
							     equation);
			    environment=environmentFactory(envName,mins,maxs,
							   maxPopulation,
							   mutationProbability,
							   crossoverProbability,
							   crossoverProportion,
							   dimensionality,
							   maxIterations,
							   epsilonX,epsilonY,
							   equation);
			    
			    analyseEvolution(environment,evalFunction,stream);
	
			      if(environment)
				delete environment;
			      if(evalFunction)
				delete evalFunction;

			  }
			else
			  {
			    cout<<flags<<endl;
			  }
		    }
		    catch(std::exception &ex)
		      {
			cerr<<ex.what()<<endl;
		      }
		    break;
		  }
		case 11:
		  {
		    cout<<"minimum distance between two best specimens of "
		      "subsequent generations: ";
		    cin>>epsilonX;
		    if(!cin.good())
		      {		
			cerr<<"Error, couldn't read the value"<<endl;
			epsilonX=0;
			cin.clear();
		      }
		    break;
		  }
		case 12:
		  {
		    cout<<"minimum fitness value difference between two best" 
		      "specimens of subsequent generations: ";
		    cin>>epsilonY;
		    if(!cin.good())
		      {		
			cerr<<"Error, couldn't read the value"<<endl;
			epsilonY=0;
			cin.clear();
		      }
		    break;
		  }
		case 13:
		  {
		    cout<<"output file: ";
		    cin>>fileName;
		    if(!cin.good())
		      {		
			cerr<<"Error, couldn't read the value"<<endl;
			fileName.clear();
			cin.clear();
		      }
		    else
		      {
			stream.open(fileName.c_str());
			if(!stream.is_open())
			  {
			    cerr<<"Error, couldn't open the file"<<endl;
			    fileName.clear();
			    stream.clear();
			  }
		      }
		    break;
		  }
		}
	    }
	}
    }
  if(environment)
    delete environment;
  if(evalFunction)
    delete evalFunction;
  if(stream.is_open())
    stream.close();
  return EXIT_SUCCESS;
}
  

 
